﻿/*******************************************************************************
 * AAEngine
 * Copyright (c) 2010 Mike Jarosch
 *
 * This file is licensed under the terms of the MIT license, which is included
 * in the "MIT License.txt" file at the root directory of this SDK.
 ******************************************************************************/

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using AAEngine.Engine.Core;
using AAEngine.Engine.Debug;
using AAEngine.Engine.Entities;
using AAEngine.Engine.Resources;
using AAEngine.Engine.Serialization;
using AAEngine.Rendering2D;
using AAEngine.Screens;
using AAEngine.Sound;

namespace AAEngine.Engine
{
    public class AAGame : Game
    {
        /// <summary>
        /// Set this to true to get rid of a bunch of development related functionality that isn't
        /// needed in a final release build.
        /// </summary>
        public static bool IS_SHIPPING_BUILD = false;

        private static AAGroup _currentGroup;

        /// <summary>
        /// Allocates an instance of the hidden Entity class. This should be
        /// used anytime an IEntity object needs to be created. Encapsulating
        /// the Entity class forces code to use IEntity rather than Entity when
        /// dealing with entity references. This will ensure that code is future         
        /// proof as well as allow the Entity class to be pooled in the future.
        /// </summary>
        /// <returns>A new IEntity</returns>
        public static IEntity NewEntity()
        {
            return Entity.New();       
        }

        public static void RegisterAssembly(string assemblyName)
        {
            TypeUtility.RegisterAssembly(assemblyName);
        }

        /// <summary>
        /// Returns the ProcessManager instance
        /// </summary>
        public static ProcessManager ProcessManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the ScreenManager instance
        /// </summary>
        public static ScreenManager ScreenManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the NameManager instance
        /// </summary>
        public static NameManager NameManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the ObjectTypeManager instance
        /// </summary>
        public static ObjectTypeManager ObjectTypeManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the InputManager instance
        /// </summary>
        public static InputManager InputManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the TemplateManager instance
        /// </summary>
        public static TemplateManager TemplateManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the LevelManager instance
        /// </summary>
        public static LevelManager LevelManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the ResourceManager instance
        /// </summary>
        public static ResourceManager ResourceManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// Returns the SoundManager instance
        /// </summary>
        public static ISoundManager SoundManager
        {
            get;
            protected set;
        }

        /// <summary>
        /// The root group; all sets and groups should be ultimately owned by
        /// this guy.
        /// </summary>
        public static AAGroup RootGroup
        {
            get;
            protected set;
        }

        /// <summary>
        /// AAObjects are automatically added to the currentGroup when they are
        /// created, so that nothing is every unrooted in the AAObject tree. You  
        /// can set this to whatever you like.
        /// </summary>
        public static AAGroup CurrentGroup
        {
            get { return _currentGroup; }
            set
            {
                if (value == null)
                {
                    throw new AAException("You cannot set the CurrentGroup to null. It must always be a valid AAGroup.");
                }

                _currentGroup = value;
            }
        }

        public static ContentManager EngineContent
        {
            get;
            protected set;
        }

        public AAGame() : base()
        {
            EngineContent = new ContentManager(Services, "AAEngineContent");

            InitializeManagers();

            // TODO: Version build, SDK, OS, etc
            //Logger.Print(this, _versionDetails.toString());

            Logger.Startup(this);
        }

        private void InitializeManagers()
        {
            ProcessManager = new ProcessManager(this);
            ScreenManager = new ScreenManager(this);
            NameManager = new NameManager();
            ObjectTypeManager = new ObjectTypeManager();
            InputManager = new InputManager();
            TemplateManager = new TemplateManager();
            LevelManager = new LevelManager();
            ResourceManager = new ResourceManager(Services, "Content");
            SoundManager = new SoundManager();
            ProcessManager.AddTickedObject(SoundManager as SoundManager, 100);

            RootGroup = new AAGroup();
            CurrentGroup = RootGroup;
            RootGroup.Initialize("RootGroup");

            Components.Add(ProcessManager);
            Components.Add(ScreenManager);
        }

        public static IEntity InitializeScene(SceneView2D sceneView, string sceneName, Type sceneClass, Type spatialManagerClass)
        {
            if (String.IsNullOrEmpty(sceneName))
            {
                sceneName = "SceneDB";
            }

            IEntity scene = NewEntity();
            scene.Initialize(sceneName);

            if (spatialManagerClass == null)
            {
                spatialManagerClass = typeof(BasicSpatialManager2D);
            }

            ISpatialManager2D spatial = TypeUtility.Instantiate(spatialManagerClass) as ISpatialManager2D;

            SpatialManager = spatial;
            scene.AddComponent(spatial as IEntityComponent, "Spatial");

            if (sceneClass == null)
            {
                sceneClass = typeof(BasicScene2D);
            }

            Scene = TypeUtility.Instantiate(sceneClass) as IScene2D;
            scene.AddComponent(Scene as IEntityComponent, "Scene");

            sceneView.Scene = Scene;

            return scene;
        }

        public static ISpatialManager2D SpatialManager
        {
            get;
            protected set;
        }

        public static IScene2D Scene
        {
            get;
            protected set;
        }

        /// <summary>
        /// Locate a AAObject (entity, set, group) by its name.
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public static AAObject Lookup(string objectName)
        {
            return NameManager.Lookup(objectName) as AAObject;
        }

        /// <summary>
        /// Locate an IEntity by its name.
        /// </summary>
        /// <param name="entityName"></param>
        /// <returns></returns>
        public static IEntity LookupEntity(string entityName)
        {
            return NameManager.Lookup(entityName) as IEntity;
        }

        /// <summary>
        /// Locate a named component on a named entity.
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="componentName"></param>
        /// <returns></returns>
        public static IEntityComponent LookupComponentByName(string entityName, string componentName)
        {
            return NameManager.LookupComponentByName(entityName, componentName);
        }

        /// <summary>
        /// Locate the first component of a type on a named entity.
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="componentType"></param>
        /// <returns></returns>
        public static IEntityComponent LookupComponentByType(string entityName, Type componentType)
        {
            return NameManager.LookupComponentByType(entityName, componentType);
        }
    }
}
